package file

import (
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"strings"
	"time"

	"l_net/pkg/msg"

	"l_net/pkg/server"
)

type Server interface {
	Start() error
}

type Client interface {
	SendFile() error
	GetConn() server.NetConn
}

type ConMgr struct {
	conn     server.NetConn
	dir      string
	fileName string

	// 发送的文件列表
	sendFiles []string

	// 在发送重要消息的时候，需要同步等待消息的状态，返回是否正确
	waitNotify chan bool
	stop       chan struct{}
	watcher    *Watch
	isMoniter  bool
}

func NewServer(conn server.NetConn, dir string, w *Watch) Server {
	c := &ConMgr{
		conn:    conn,
		dir:     dir,
		stop:    make(chan struct{}),
		watcher: w,
	}

	if w != nil {
		c.isMoniter = true
	} else {
		c.isMoniter = false
	}
	return c
}

func (c *ConMgr) Start() error {
	defer func() {
		fmt.Println("end a connection:")
	}()
	c.conn.HandlerLoop()
	// 处理接收的消息
	return c.handler()
}

// 包装创建文件夹函数
func MkDirs(dirPath string, perm os.FileMode) error {
	err := os.MkdirAll(dirPath, perm)
	if err != nil {
		log.Fatal("这是什么玩意：", dirPath)
		return err
	}
	return nil
}

func (c *ConMgr) handler() error {
	var fs *os.File
	var err error
	var dirPath string

	defer func() {
		println("一次文件传输完成！！")
		if fs != nil {
			_ = fs.Close()
		}
	}()

	for !c.conn.IsClose() {
		m, ok := c.conn.GetMsg()
		if !ok {
			return fmt.Errorf("close by connect")
		}
		if m == nil {
			continue
		}

		switch m.MsgType {
		case msg.MsgHead:
			// 创建文件
			nameAndDir := strings.Split(m.FileName, "|")
			if len(nameAndDir) != 0 {
				dirPath = nameAndDir[0]              //获取文件的文件夹路径
				m.FileName = dirPath + nameAndDir[1] //覆盖原有的数据，保证不改动源代码
			} else {
				fmt.Println("occ mistake : resolve direct and file name ")
				c.conn.SendMsg(msg.NewNotifyMsg(c.fileName, msg.Status_Err)) //出现错误的话发送信息关闭连接
				return nil
			}
			//创建文件路径
			err := MkDirs(filepath.Join(c.dir, dirPath), 0777) //create a new file
			if err != nil {
				fmt.Println("文件夹创建失败：", err)
				c.conn.SendMsg(msg.NewNotifyMsg(c.fileName, msg.Status_Err))
				return nil
			}
			fmt.Println("文件夹创建成功")

			if m.FileName != "" {
				c.fileName = m.FileName
			} else {
				c.fileName = GenFileName() //uuid
			}

			fmt.Println("recv head fileName is", c.fileName)
			fs, err = os.OpenFile(filepath.Join(c.dir, c.fileName), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777)
			openfile, _ := filepath.Abs(filepath.Join(c.dir, c.fileName))
			c.watcher.RcvFileList.Add(openfile)
			fmt.Println("open file name is : ", openfile)
			if err != nil {
				fmt.Println("os.Create err =", err)
				c.conn.SendMsg(msg.NewNotifyMsg(c.fileName, msg.Status_Err))
				return err
			}
			fmt.Println("send head is ok")

			c.conn.SendMsg(msg.NewNotifyMsg(c.fileName, msg.Status_Ok))
		case msg.MsgFile:
			if fs == nil {
				fmt.Println(c.fileName, "file is not open !")
				c.conn.SendMsg(msg.NewCloseMsg(c.fileName, msg.Status_Err))
				return nil
			}
			// 写入文件
			_, err = fs.Write(m.Bytes)
			if err != nil {
				fmt.Println("file.Write err =", err)
				c.conn.SendMsg(msg.NewCloseMsg(c.fileName, msg.Status_Err))
				return err
			}
		case msg.MsgEnd:
			// 操作完成
			info, _ := fs.Stat()
			if info.Size() != int64(m.Size) {
				err = fmt.Errorf("file.size %v rece size %v \n", info.Size(), m.Size)
				c.conn.SendMsg(msg.NewCloseMsg(c.fileName, msg.Status_Err))
				return err
			}

			fmt.Printf("save file %v is success \n", info.Name())
			c.conn.SendMsg(msg.NewNotifyMsg(c.fileName, msg.Status_Ok))

			fmt.Printf("close file %v is success \n", c.fileName)
			_ = fs.Close()
			fs = nil
		case msg.MsgNotify:
			c.waitNotify <- m.Bytes[0] == byte(msg.Status_Ok)
		case msg.MsgRename:
			fmt.Println("rcv rename msg !!!, sheared space dir :", c.dir)
			names := strings.Split(m.FileName, "|")
			oldName, newName := names[0], names[1]
			fmt.Println("old name: ", oldName, " ----- new name: ", newName)
			//添加到rcvlist
			absFilePath, _ := filepath.Abs(filepath.Join(c.dir, oldName))
			c.watcher.RcvFileList.Add(absFilePath)
			//重命名文件/文件夹
			err := os.Rename(absFilePath, filepath.Join(c.dir, newName))
			fmt.Println("add revfileList : ", absFilePath)
			//移除老文件夹的监控，为什么不需要判断是否为文件可以看Watcher类
			if c.isMoniter {
				c.watcher.Watch.Remove(filepath.Join(c.dir, oldName))
			}
			if err != nil {
				fmt.Println("重命名文件夹时出错: ", err)
			}
			c.conn.SendMsg(msg.NewCloseMsg(c.fileName, msg.Status_Ok))

		case msg.MsgRemove:
			fmt.Println("rcv remove msg !!!, sheared space dir :", c.dir)
			delFile := filepath.Join(c.dir, m.FileName)
			fmt.Println("ready del file name is : ", delFile)
			//加入到list
			absFilePath, _ := filepath.Abs(delFile)
			c.watcher.RcvFileList.Add(absFilePath)
			//删除文件/文件夹
			err := os.RemoveAll(delFile)
			if err != nil {
				fmt.Println("删除文件夹时出错: ", err)
			}
			//判断是否为文件移除监控
			fi, err := os.Stat(delFile)
			if c.isMoniter && err == nil && fi.IsDir() {
				c.watcher.Watch.Remove(delFile)
				fmt.Println("删除监控 : ", delFile)
			}
			c.conn.SendMsg(msg.NewCloseMsg(c.fileName, msg.Status_Ok))

		case msg.MsgClose:
			fmt.Printf("revc close msg ....\n")
			fmt.Println("ready close connect .... ")
			c.conn.Close()
			if c.conn.IsClose() {
				fmt.Println("close success")
			} else {
				fmt.Println("close be defeated")
			}
			if m.Bytes[0] != byte(msg.Status_Ok) {
				return fmt.Errorf("server an error occurred")
			}

			return nil
		default:
			fmt.Println("啥也没有")
		}
	}

	return err
}

func NewClient(conn server.NetConn, dir string, files []string) Client {
	return &ConMgr{
		conn:       conn,
		dir:        dir,
		sendFiles:  files,
		waitNotify: make(chan bool, 1),
		stop:       make(chan struct{}),
	}
}

func (c *ConMgr) GetConn() server.NetConn {
	c.conn.HandlerLoop()
	// 处理接收的消息
	go func() {
		_ = c.handler()
	}()
	return c.conn
}

func (c *ConMgr) SendFile() error {
	var err error
	c.conn.HandlerLoop()
	// 处理接收的消息
	go func() {
		_ = c.handler()
	}()
	err = c.sendFile()
	return err
}

func (c *ConMgr) sendFile() error {
	defer func() {
		_ = c.conn.Close()
	}()

	var err error
	for _, file := range c.sendFiles {
		err = c.sendSingleFile(filepath.Join(c.dir, file))
		if err != nil {
			return err
		}
	}

	c.conn.SendMsg(msg.NewCloseMsg(c.fileName, msg.Status_Ok))
	time.Sleep(50 * time.Millisecond)
	return err
}

func (c *ConMgr) sendSingleFile(filePath string) error {
	file, err := os.Open(filePath)

	if err != nil {
		fmt.Printf("open file err %v \n", err)
		return err
	}

	defer func() {
		if file != nil {
			_ = file.Close()
		}
	}()
	//找到文件后，这里的逻辑需要去删除共享目录和文件民，只拿去中间的文件夹路径
	fileInfo, _ := file.Stat()
	fileName := fileInfo.Name()
	absFilePath, _ := filepath.Abs(filePath)

	dirPath := GetMidDirByAbsPath(c.dir, absFilePath)

	// absDir, _ := filepath.Abs(c.dir)
	// fmt.Println("absFilePath: ", absFilePath)
	// fmt.Println("absDir: ", absDir)
	// fmt.Println("fileName: ", fileName)
	// dirPath := absFilePath[:len(absFilePath)-len(fileName)]
	// dirPath = dirPath[len(absDir):]
	// fmt.Println("---------dirPath------: ", dirPath)

	// dir = fileName[len(dirAsb):]
	// dir = dir[:strings.Index(dir, file_name)]
	// fmt.Println("更改后的dir为: ", dir)
	fileAndDir := dirPath + "|" + fileName

	fmt.Println("client ready to write ", fileAndDir)
	m := msg.NewHeadMsg(fileAndDir)
	// 发送文件信息
	c.conn.SendMsg(m)

	// 等待服务器返回通知消息
	timer := time.NewTimer(5 * time.Second)
	select {
	case ok := <-c.waitNotify:
		if !ok {
			return fmt.Errorf("send err")
		}
	case <-timer.C:
		return fmt.Errorf("wait server msg timeout")
	}

	for !c.conn.IsClose() {
		// 发送文件数据
		readBuf := msg.BytesPool.Get().([]byte)

		n, err := file.Read(readBuf)
		if err != nil && err != io.EOF {
			return err
		}

		if n == 0 {
			break
		}

		c.conn.SendMsg(msg.NewFileMsg(c.fileName, readBuf[:n]))
	}

	c.conn.SendMsg(msg.NewEndMsg(c.fileName, uint64(fileInfo.Size())))

	// 等待服务器返回通知消息
	timer = time.NewTimer(5 * time.Second)
	select {
	case ok := <-c.waitNotify:
		if !ok {
			return fmt.Errorf("send err")
		}
	case <-timer.C:
		return fmt.Errorf("wait server msg timeout")
	}

	fmt.Println("client send " + filePath + " file success...")
	return nil
}
